﻿/**
 *
 * net.c
 *
 *  Created on: 2019-11-27
 *      Author: chenshisheng
 */

#include "net.h"
#include "at_command.h"
#include "ctimer.h"
#include "hw.h"
#include "resource.h"

#include <stdlib.h>

enum _Step
{
    _Step_Min,

    _Step_Reboot = _Step_Min,
    _Step_At,
    _Step_NetRegister,
    _Step_Create,
    _Step_AddObj,
    _Step_DiscoverRsp,
    _Step_Open,
    _Step_OpenSended,
    _Step_Notify,

    _Step_Max
};

static struct
{
    int retryNum;
    int retryCount;
    enum _Step step;
    unsigned int seqDelay;
    unsigned int retryDelay;
    Ctimer_t seqTimer;
//    Ctimer_t lifeTimer;
    unsigned int lifetime;
    bool isAbort;
}_seqCtrl;

const struct
{
    unsigned int seqDelay;
    int retryNum;
    unsigned int retryDelay;
}_seqProfiles[_Step_Max] =
{
        {6000,  1,  1000},
        {100,   5,  2000},
        {100,   20, 3000},
        {100,   2,  1000},
        {100,   5,  1000},
        {100,   5,  1000},
        {100,   5,  1000},
        {100,   1,  1000},
        {100,   5,  2000},
};

void Net_Init(void)
{
//    _seqCtrl.needNotify = false;
    _seqCtrl.step = _Step_At;

    HW_Config(HW_Out_NetEn, HW_State_Active);
    HW_Config(HW_Out_NetRst, HW_State_Active);
    HAL_Delay(100);
    HW_Config(HW_Out_NetRst, HW_State_Inactive);
}

static void _OnLifeTimeout(int data)
{
    _seqCtrl.step = _Step_Open;
//    Ctimer_Stop(&_seqCtrl.lifeTimer);
    debug("Net: Life timeout!\n");
    if(Resource_IsNeedNotify())
    {
        Net_StartNotify();
    }
}

int Net_MiplOpen(int lifetime, int timeout)
{
    int ret;

    _seqCtrl.lifetime = lifetime;
    ret = AtCommand_MiplOpen(lifetime, timeout);
    return ret;
}

static int _NotifySequence(enum _Step step)
{
    int ret = -1;

    switch(step)
    {
    case _Step_Reboot:
        ret = AtCommand_Reboot();
        ON_RELEASE(printf("Net: Reset!\n");)
        break;

    case _Step_At:
        ret = AtCommand_At();
        break;

    case _Step_NetRegister:
    {
        AtCommand_NetworkRegState_t state;

        state = AtCommand_ReadNetworkRegState();
        ret = ((state == AtCommand_NetworkRegState_HomeRegistered) ||
                (state == AtCommand_NetworkRegState_RoamingRegistered)) ? 0 : -1;
        if(ret == 0)
        {
            ON_RELEASE(printf("Net: Connected to base station.\n");)
        }
    }
        break;

    case _Step_Create:
        ret = Resource_MiplCreate();
        if(ret == 0)
        {
            ON_RELEASE(printf("Net: MIPLCREATed.\n");)
//            HAL_Delay(20);
//            AtCommand_CSCON(1);
//            AtCommand_MiplAutoUpdate(true);
        }
        break;

    case _Step_AddObj:
        ret = Resource_AddObj();
        break;

    case _Step_DiscoverRsp:
        ret = Resource_DiscoverRsp();
        break;

    case _Step_Open:
        ret = Net_MiplOpen(NET_LIFETIME, 30);
        break;

    case _Step_OpenSended:
        ret = 1;  // open成功后不能立刻notify，需要等到MiplDiscover事件后才能notify
        ON_RELEASE(printf("Net: MIPLOPENed.\n");)
        break;

    case _Step_Notify:
        ret = Resource_NotifyAll();
        break;

    default:
        break;
    }

    return ret;
}

static void _CmdSequeue(int data)
{
    int ret;

    do
    {
        ret = _NotifySequence(_seqCtrl.step);
        if(_seqCtrl.isAbort)
        {
            return;
        }

        if(ret != 0)
        {
            break;
        }

        if((_seqCtrl.step == (_Step_Max - 1)) || (_seqCtrl.step == _Step_OpenSended))
        {
            Ctimer_Stop(&_seqCtrl.seqTimer);
            return;
        }

        _seqCtrl.step ++;
        _seqCtrl.retryCount = 0;
        _seqCtrl.seqDelay = _seqProfiles[_seqCtrl.step].seqDelay;
        _seqCtrl.retryNum = _seqProfiles[_seqCtrl.step].retryNum;
        _seqCtrl.retryDelay = _seqProfiles[_seqCtrl.step].retryDelay;
        Ctimer_Start(&_seqCtrl.seqTimer, _seqCtrl.seqDelay, _CmdSequeue, CTIMER_FLAG_ONCE, 0);
        return;
    }while(0);

    if(_seqCtrl.step != _Step_OpenSended)
    {
        _seqCtrl.retryCount ++;
        if(_seqCtrl.retryCount >= _seqCtrl.retryNum)
        {
            _seqCtrl.step = _Step_Reboot;
            Ctimer_Start(&_seqCtrl.seqTimer, 10 * CTIMER_TICK_SECOND, _CmdSequeue, CTIMER_FLAG_ONCE, 0);
        }
        else
        {
            Ctimer_Start(&_seqCtrl.seqTimer, _seqCtrl.retryDelay, _CmdSequeue, CTIMER_FLAG_ONCE, 0);
        }
    }
    else
    {
        Ctimer_Stop(&_seqCtrl.seqTimer);
    }
}

static inline bool _IsSequenceActive(void)
{
    return Ctimer_IsRunning(& _seqCtrl.seqTimer);
}

void Net_StartNotify(void)
{
    if(_IsSequenceActive())
    {
        return;
    }

    _seqCtrl.retryCount = 0;
    _seqCtrl.retryNum = _seqProfiles[_seqCtrl.step].retryNum;
    _seqCtrl.retryDelay = _seqProfiles[_seqCtrl.step].retryDelay;
    _CmdSequeue(0);
}

void Net_MiplUpdate(int lifetime)
{
    _seqCtrl.lifetime = lifetime;
    if(_seqCtrl.step < _Step_OpenSended)
    {
        Net_StartNotify();
    }
    else
    {
        AtCommand_MiplUpdate(lifetime);
    }
}

int Net_NotifyAbortCmd(Stream_t *stream, int argc, char *argv[])
{
    if(_seqCtrl.step != _Step_OpenSended)
    {
        _seqCtrl.isAbort = true;
        Ctimer_Stop(&_seqCtrl.seqTimer);
        _seqCtrl.step = _Step_OpenSended;
    }

    return 0;
}

void Net_OnMiplEvent(const MsgQueue_Msg_t *msg)
{
    AtCommand_MiplEvent_t event;

    event = *((AtCommand_MiplEvent_t *)msg->data);
    switch(event)
    {
    case AtCommand_MiplEvent_BootstrapSuccess:
        break;

    case AtCommand_MiplEvent_BootstrapFail:
    case AtCommand_MiplEvent_RegisterFail:
        _seqCtrl.step = _Step_Reboot;
        if(Resource_IsNeedNotify())
        {
            Net_StartNotify();
        }
        break;

    case AtCommand_MiplEvent_RegisterSuccess:
    case AtCommand_MiplEvent_UpdateSuccess:
        _seqCtrl.step = _Step_Notify;
//        Ctimer_Start(
//                &_seqCtrl.lifeTimer,
//                _seqCtrl.lifetime * CTIMER_TICK_SECOND,
//                _OnLifeTimeout,
//                CTIMER_FLAG_ONCE, 0);
        break;

    case AtCommand_MiplEvent_CancleSuccess:
        _OnLifeTimeout(0);
        break;

    case AtCommand_MiplEvent_NotifyAck:
        _seqCtrl.step = _Step_Notify;
        break;

    default:
        debug("Net: Unknown MiplEvent: %d\n", (int)event);
        break;
    }

    ON_RELEASE(printf("Net: MiplEvent: %d\n", (int) event);)
}

void Net_OnMiplDiscoverObj(const MsgQueue_Msg_t *msg)
{
//    if(! Ctimer_IsRunning(&_seqCtrl.lifeTimer))
//    {
//        Ctimer_Start(
//                        &_seqCtrl.lifeTimer,
//                        _seqCtrl.lifetime * CTIMER_TICK_SECOND,
//                        _OnLifeTimeout,
//                        CTIMER_FLAG_ONCE, 0);
//        debug("Net: Error: event 'Register Success' not received!\n");
//    }
}
